//Project name: personal-diary-bm
//Author: Brunner Márton (YV2X56)

//Ebben az állományban a naplót mint fájlt kezelő metódusok szerepelnek, amelyekkel a fájlból való beolvasás és a fájlba való kiíratás valósítható meg
#ifndef BMFILEHANDLING_HPP
#define BMFILEHANDLING_HPP

#include <iostream>
#include <string>
#include "diary.hpp"

enum DiaryOpenError{NoSuchFile, WrongFile, WrongPassword}; //A naplómegnyitáskor felmerülõ hibák

const std::string toFileName(std::string &s);
void SaveDiary(std::string &user, std::string &pass, std::vector<Entry*> &diary);
void LoadDiary(std::string &user, std::string &pass, std::vector<Entry*> &diary, const bool debug=false) throw (DiaryOpenError);

//Egy felhasználónévből, a hozzá tartozó fájlnevet adja vissza.
const std::string toFileName(std::string &s){
    return (s+".pd");
}

//Karakterré alakító metódus
template <typename T>
unsigned char toChar(T a){
    unsigned char tmp=a;
    return tmp;
}

//Egész számot ír ki a megadott fájlba, kódolva azt a megadott jelszó és pozició segítségével
void intToFile(std::ofstream &f, std::string &pass, unsigned int &pos, const int &mit){
    f << toChar(toChar<int>(mit)^pass[pos]);
    pos=(pos==pass.length()-1)?0:pos+1;
}

//Egy szöveget ír ki a megadott fájlba, kódolva azt a megadott jelszó és pozíció segítségével
void stringToFile(std::ofstream &f, std::string &pass, unsigned int &pos, const std::string &mit){
    for(unsigned int i=0;i<mit.length();++i){
        f << toChar(pass[pos]^mit[i]);
        pos=(pos==pass.length()-1)?0:pos+1;
    }
}

//Napló kimentése a felhasználónév alapján meghatározott fájlba
void SaveDiary(std::string &user, std::string &pass, std::vector<Entry*> &diary){
    std::ofstream f;
    f.open(toFileName(user).c_str(), std::ifstream::binary);
    //Főfejléc létrehozása
    f << '\1';
    unsigned char tmp = ~(pass.length()); //Jelszó hosszának megállapítása
    f << tmp;
    for(unsigned int i=0;i<pass.length();i++){
        unsigned char tmp = ~pass[i];
        f << tmp;
    }
    //Bejegyzések kiírása
    int id = 1;
    for(unsigned int i=0;i<diary.size();++i){
        unsigned int pos = 0;
        intToFile(f, pass, pos, id/16);//Sorszám 1. bájtja
        intToFile(f, pass, pos, id%16);//Sorszám 2. bájtja
        intToFile(f, pass, pos, diary.at(i)->EntryType());//Bejegyzéstípus

        //Dátum kiírása
        intToFile(f, pass, pos, diary.at(i)->getDate().getYear()/1000);
        intToFile(f, pass, pos, diary.at(i)->getDate().getYear()%1000/100);
        intToFile(f, pass, pos, diary.at(i)->getDate().getYear()%100/10);
        intToFile(f, pass, pos, diary.at(i)->getDate().getYear()%10);
        intToFile(f, pass, pos, diary.at(i)->getDate().getMonth()/10);
        intToFile(f, pass, pos, diary.at(i)->getDate().getMonth()%10);
        intToFile(f, pass, pos, diary.at(i)->getDate().getDay()/10);
        intToFile(f, pass, pos, diary.at(i)->getDate().getDay()%10);

        intToFile(f, pass, pos, diary.at(i)->getName().length());//Név hosszának kiírása
        if(diary.at(i)->EntryType()==1){//Rövid szöveges bejegyzés
            ShortEntry* tmp = dynamic_cast <ShortEntry*> (diary.at(i));
            intToFile(f, pass, pos, tmp->getText().length());//Szöveg hosszának kiírása
            stringToFile(f, pass, pos, tmp->getName());//Név kiírása
            stringToFile(f, pass, pos, tmp->getText());//Szöveg kiírása
        }else if(diary.at(i)->EntryType()==2){//Hosszú szöveges bejegyzés
            LongEntry* tmp = dynamic_cast <LongEntry*> (diary.at(i));
            intToFile(f, pass, pos, tmp->getText().length());//Szöveg hosszának kiírása
            intToFile(f, pass, pos, tmp->getBrief().length());//Rövid leírás hosszának kiírása
            stringToFile(f, pass, pos, tmp->getName());//Név kiírása
            stringToFile(f, pass, pos, tmp->getBrief());//Rövid leírás kiírása
            stringToFile(f, pass, pos, tmp->getText());//Szöveg kiírása
        }else if(diary.at(i)->EntryType()==3){//Webcím bejegyzés
            WebAddress* tmp = dynamic_cast <WebAddress*> (diary.at(i));
            intToFile(f, pass, pos, tmp->getWeblink().length());//Webcím hosszának kiírása
            intToFile(f, pass, pos, tmp->getBrief().length());//Rövid leírás hosszának kiírása
            stringToFile(f, pass, pos, tmp->getName());//Név kiírása
            stringToFile(f, pass, pos, tmp->getBrief());//Rövid leírás kiírása
            stringToFile(f, pass, pos, tmp->getWeblink());//Webcím kiírása
        }
        ++id;
    }
    f.close();
}

//A megadott fájlból a jelszó alapján dekódolt egész számot adja vissza
int intFromFile(std::ifstream &f, std::string &pass, unsigned int &pos){
    unsigned char tmp;
    f >> std::noskipws >> tmp;
    tmp^=pass[pos];
    pos=(pos==pass.length()-1)?0:pos+1;
    return tmp;
}

//A megadott fájlból a jelszó alapján dekódolt szöveget adja vissza eredményül
std::string stringFromFile(std::ifstream &f, std::string &pass, unsigned int &pos, unsigned int length){
    unsigned char tmp;
    std::string ret;
    for(unsigned int i=0;i<length;++i){
        f >> std::noskipws >> tmp;
        tmp^=pass[pos];
        pos=(pos==pass.length()-1)?0:pos+1;
        ret+=tmp;
    }
    return ret;
}

//A fájlból való naplóbetöltést végzi el
void LoadDiary(std::string &user, std::string &pass, std::vector<Entry*> &diary, const bool debug) throw (DiaryOpenError){
    std::ifstream f;
    f.open(toFileName(user).c_str(), std::ifstream::binary);
    if(f.is_open()){
        unsigned char ch;
        //Fájlverzió vizsgálat, ha késõbb további verziók lesznek azt itt kell lekezelni
        f >> ch;
        if(ch!='\1') throw WrongFile;

        //A jelszó hosszának beolvasása
        f >> ch;
        ch=~ch;
        unsigned int plength = ch;
        if(debug) std::cout << "A fajl alapjan elvart jelszohossz: " << plength << std::endl;

        //Jelszó ellenõrzése
        unsigned int i = 0;
        f >> ch;
        ch=~ch;
        while(i<plength && ch==pass[i]){
            ++i;
            if(i<plength){
                f >> ch;
                ch=~ch;
            }
        }
        if(i<plength) throw WrongPassword;

        while(!f.eof()){
            if(debug) std::cout << "\nUj bejegyzes beolvasasanak kezdete\n";
            Entry* en;
            unsigned int pos = 0;
            //Sorszám beolvasása (mivel jelen esetben nem használjuk fel a sorszámot ezért azt nem is tároljuk)
            intFromFile(f, pass, pos);
            intFromFile(f, pass, pos);
            //Típus beolvasása
            int type=intFromFile(f, pass, pos);
            //Dátum beolvasása
            int year_tmp=intFromFile(f, pass, pos)*1000+intFromFile(f, pass, pos)*100+intFromFile(f, pass, pos)*10+intFromFile(f, pass, pos);
            int month_tmp=intFromFile(f, pass, pos)*10+intFromFile(f, pass, pos);
            int day_tmp=intFromFile(f, pass, pos)*10+intFromFile(f, pass, pos);
            Date date(year_tmp, month_tmp, day_tmp);

            int name_length=intFromFile(f, pass, pos);//Név hosszának beolvasása
            if(type==1){//Rövid szöveges bejegyzés beolvasása
                int text_length=intFromFile(f, pass, pos);//Szöveg hosszának beolvasása
                std::string name=stringFromFile(f, pass, pos, name_length);//Név beolvasása
                std::string text=stringFromFile(f, pass, pos, text_length);//Szöveg beolvasása
                if(debug) std::cout << "Rovid bejegyzes n<" << name << "> t<" << text << "> d<" << date << ">" << std::endl;
                en = new ShortEntry(name, date, text);
            }else if(type==2){//Hosszu szöveges bejegyzés beolvasása
                int text_length=intFromFile(f, pass, pos);//Szöveg hosszának beolvasása
                int brief_length=intFromFile(f, pass, pos);//Rövid leírás hosszának beolvasása
                std::string name=stringFromFile(f, pass, pos, name_length);//Név beolvasása
                std::string brief=stringFromFile(f, pass, pos, brief_length);//Rövid leírás beolvasása
                std::string text=stringFromFile(f, pass, pos, text_length);//Szöveg beolvasása
                en = new LongEntry(name, date, text, brief);
                if(debug) std::cout << "Hosszu bejegyzes n(" << name_length << ")<" << name << "> t(" << text_length << ")<" << text << "> d<" << date << "> b(" <<brief_length << ")<" << brief  << ">" << std::endl;
            }else if(type==3){//Webcím beolvasása
                int web_length=intFromFile(f, pass, pos);//Webcím hosszának beolvasása
                int brief_length=intFromFile(f, pass, pos);//Rövid szöveges leírás hosszának beolvasása
                std::string name=stringFromFile(f, pass, pos, name_length);//Név beolvasása
                std::string brief=stringFromFile(f, pass, pos, brief_length);//Rövid leírás beolvasása
                std::string web=stringFromFile(f, pass, pos, web_length);//Webcím beolvasása
                en = new LongEntry(name, date, web, brief);
                if(debug) std::cout << "Weblink bejegyzes n<" << name << "> w<" << web << "> d<" << date << "> b<" << brief << ">" << std::endl;
            }
            if(!f.fail()) diary.push_back(en);//Amennyiben sikeresen be tudott olvasni minden adatot, úgy hozzáadja  avectorhoz.
        }
    }else throw NoSuchFile;
}

#endif // BMFILEHANDLING_HPP
